#include "diagfuncs.h"
#include "qgcmio.h"
#include "main_aux.h"
#include <mpParser.h>

#define RETURNDIAG(arg)  { (*ret) = ( arg ); return; }
#define getarg(index) (*(argv[index].Get()))
// ..........................................................................
// list of builtins:
void diag_builtin_abs_complex(diag_builtin_call_stack){
	const cmplx_type & cval = getarg(0).GetComplex();
	RETURNDIAG( static_cast<float_type>(abs(cval)) );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_abs_int(diag_builtin_call_stack){
	const int_type ival = getarg(0).GetInteger();
	RETURNDIAG( ival < 0 ? -ival : ival ); 
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_abs_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>(fabs(val)) ); 
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_sin_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( sin(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_cos_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( cos(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_tan_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( tan(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_asin_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( asin(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_acos_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( acos(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_atan_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( atan(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_sinh_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( sinh(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_cosh_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( cosh(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_tanh_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( tanh(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_ln_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( log(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_log10_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( log10(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_exp_real(diag_builtin_call_stack){
	const float_type val = getarg(0).GetFloat();
	RETURNDIAG( static_cast<float_type>( exp(val) ) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_exp_complex(diag_builtin_call_stack){
	const cmplx_type & val = (*argv[0].Get()).GetComplex();
	RETURNDIAG( exp(val) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_sqrt_complex(diag_builtin_call_stack){
	const float_type val = (*argv[0].Get()).GetFloat();
	RETURNDIAG( sqrt( cmplx_type(val, 0) ) );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_sqrt_real(diag_builtin_call_stack){
	const float_type val = (*argv[0].Get()).GetFloat();
	RETURNDIAG( static_cast<float_type>(sqrt( val ) ) );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_real_complex(diag_builtin_call_stack){
	const cmplx_type & val = (*argv[0].Get()).GetComplex();
	RETURNDIAG( static_cast<float_type>( val.real() ) );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_imag_complex(diag_builtin_call_stack){
	const cmplx_type & val = (*argv[0].Get()).GetComplex();
	RETURNDIAG( static_cast<float_type>( val.imag() ) );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_conj_complex(diag_builtin_call_stack){
	const cmplx_type & val = (*argv[0].Get()).GetComplex();
	RETURNDIAG( conj( val ) );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_min_int(diag_builtin_call_stack){
	int_type rval = getarg(0).GetInteger();
	for(int iter = 1; iter < argc; iter++){
		const int_type arg = getarg(iter).GetInteger();
		if( arg < rval ) rval = arg;
	}
	RETURNDIAG( rval );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_min_real(diag_builtin_call_stack){
	float_type rval = getarg(0).GetFloat();
	for(int iter = 1; iter < argc; iter++){
		const float_type arg = getarg(iter).GetFloat();
		if( arg < rval ) rval = arg;
	}
	RETURNDIAG( rval );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_max_int(diag_builtin_call_stack){
	int_type rval = getarg(0).GetInteger();
	for(int iter = 1; iter < argc; iter++){
		const int_type arg = getarg(iter).GetInteger();
		if( arg > rval ) rval = arg;
	}
	RETURNDIAG( rval );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_max_real(diag_builtin_call_stack){
	float_type rval = getarg(0).GetFloat();
	for(int iter = 1; iter < argc; iter++){
		const float_type arg = getarg(iter).GetFloat();
		if( arg > rval ) rval = arg;
	}
	RETURNDIAG( rval );
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_sum_int(diag_builtin_call_stack){
	int_type rval = 0;
	for(int iter = 0; iter < argc; iter++){
		rval += getarg(iter).GetInteger();
	}
	RETURNDIAG( rval );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_sum_real(diag_builtin_call_stack){
	float_type rval = 0;
	for(int iter = 0; iter < argc; iter++){
		rval = rval+getarg(iter).GetFloat();
	}
	RETURNDIAG( rval );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_length_string(diag_builtin_call_stack){
	const string_type & str = getarg(0).GetString();
	RETURNDIAG( static_cast<int_type>(str.size()) );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void diag_builtin_length_array(diag_builtin_call_stack){
	RETURNDIAG( static_cast<int>( getarg(0).GetArray().size() ) );
}
// ..........................................................................
// end of built ins ...

// ..........................................................................
// binary operators:
#define arg1 (*(arg1ptr))
#define arg2 (*(arg2ptr))

void diag_bin_oprt_sharp(diag_bin_call_stack){
	// takes arg1 arg2, RETURNDIAG( statement )
	// arg1 must be type array ( that is checked by caller );
	int_type index = arg2.GetInteger() - 1;
	const array_type & array = arg1.GetArray();
	bool isinrange = index >= 0 && index < array.size();
	if(!getModel().iscompiled()){
		if(isinrange) RETURNDIAG( array[index] );  
		RETURNDIAG( Value( sqrt(-1) ) );
	}else{
		if(! isinrange ) mpError("Index %i is out of bounds.", index+1);
		RETURNDIAG( array[index] );
	}
};

void profile_bin_oprt_sharp(diag_bin_call_stack){
	// arg1 must be type array ( that is checked by caller );
	int_type index = arg2.GetInteger() - 1;
	const array_type & array = arg1.GetArray();
	bool isinrange = index >= 0 && index <= array.size();
	if( ! isinrange ) mpError("Operator '#' attempted to access profile out of bounds.");
	RETURNDIAG( array[index] );
};

// Macro for auto generating very simple functions such as +, -, operators
#define REG_BIN_OPRT_SIMPLE(name, op, type, retrfunc) void diag_bin_oprt_##name##_##type(diag_bin_call_stack) \
{ \
	RETURNDIAG( arg1.retrfunc() op arg2.retrfunc() ); \
};

REG_BIN_OPRT_SIMPLE(add, +, int, GetInteger);
REG_BIN_OPRT_SIMPLE(add, +, complex, GetComplex); 
REG_BIN_OPRT_SIMPLE(add, +, real, GetFloat); 
REG_BIN_OPRT_SIMPLE(add, +, string, GetString);
REG_BIN_OPRT_SIMPLE(sub, -, int, GetInteger);
REG_BIN_OPRT_SIMPLE(sub, -, real, GetFloat);
REG_BIN_OPRT_SIMPLE(sub, -, complex, GetComplex);
REG_BIN_OPRT_SIMPLE(mul, *, int, GetInteger);
REG_BIN_OPRT_SIMPLE(mul, *, real, GetFloat);
REG_BIN_OPRT_SIMPLE(mul, *, complex, GetComplex);
REG_BIN_OPRT_SIMPLE(div, /, int, GetInteger);
REG_BIN_OPRT_SIMPLE(div, /, real, GetFloat);
REG_BIN_OPRT_SIMPLE(div, /, complex, GetComplex);
REG_BIN_OPRT_SIMPLE(mod, %, int, GetInteger);

void diag_bin_oprt_mod_real(diag_bin_call_stack){
	RETURNDIAG( static_cast<float_type>( static_cast<int_type>( arg1.GetFloat() ) % static_cast<int_type>( arg2.GetFloat() ) ) );
}

void diag_bin_oprt_pow_int(diag_bin_call_stack){
	RETURNDIAG( static_cast<int_type>( pow(
					arg1.GetFloat(), 
					static_cast<int_type>(arg2.GetInteger())) ) );
}; 
void diag_bin_oprt_pow_real(diag_bin_call_stack){
	RETURNDIAG( static_cast<float_type>( pow(arg1.GetFloat(), arg2.GetFloat()) ) );
}; 
void diag_bin_oprt_pow_complex(diag_bin_call_stack){
	RETURNDIAG( pow(arg1.GetComplex(), arg2.GetComplex())  );
}

// Macro for auto generating very simple functions such as >, <, operators
#define REG_CMP_OPRT_SIMPLE(name, op, type, retrfunc) void diag_bin_oprt_##name##_##type(diag_bin_call_stack) \
{ \
	RETURNDIAG( static_cast<bool_type>(arg1.retrfunc() op arg2.retrfunc()) ); \
};

REG_CMP_OPRT_SIMPLE(eq, ==, int, GetInteger);
REG_CMP_OPRT_SIMPLE(eq, ==, real, GetFloat);
REG_CMP_OPRT_SIMPLE(eq, ==, complex, GetComplex);
REG_CMP_OPRT_SIMPLE(eq, ==, string, GetString);
REG_CMP_OPRT_SIMPLE(eq, ==, bool, GetBool);
REG_CMP_OPRT_SIMPLE(eq, ==, array, GetArray); // should work (but may need an expanded body)

REG_CMP_OPRT_SIMPLE(leq,<=,int,GetInteger);
REG_CMP_OPRT_SIMPLE(leq,<=,real,GetFloat);
REG_CMP_OPRT_SIMPLE(lt,<,int,GetInteger);
REG_CMP_OPRT_SIMPLE(lt,<,real,GetFloat);
REG_CMP_OPRT_SIMPLE(geq,>=,int,GetInteger);
REG_CMP_OPRT_SIMPLE(geq,>=,real,GetFloat);
REG_CMP_OPRT_SIMPLE(gt,>,int,GetInteger);
REG_CMP_OPRT_SIMPLE(gt,>,real,GetFloat);

REG_CMP_OPRT_SIMPLE(neq,!=,int,GetInteger);
REG_CMP_OPRT_SIMPLE(neq,!=,real,GetFloat);
REG_CMP_OPRT_SIMPLE(neq,!=,complex,GetComplex);
REG_CMP_OPRT_SIMPLE(neq,!=,string,GetString);
REG_CMP_OPRT_SIMPLE(neq,!=,bool,GetBool);
REG_CMP_OPRT_SIMPLE(neq,!=,array,GetArray);

REG_CMP_OPRT_SIMPLE(and,&&,bool,GetBool);
REG_CMP_OPRT_SIMPLE(or,||,bool,GetBool);


// Macro for auto generating very simple unitary operator functions 
#define REG_UNITARY_SIMPLE_FUNC(name, op, type, retrfunc) void diag_uni_oprt_##name##_##type(diag_infix_call_stack) \
{ \
	RETURNDIAG( op static_cast<type##_type>( arg->retrfunc() ) ); \
};

REG_UNITARY_SIMPLE_FUNC(not, !, bool, GetBool);
REG_UNITARY_SIMPLE_FUNC(neg, -, float, GetFloat);
REG_UNITARY_SIMPLE_FUNC(neg, -, int, GetInteger);
void diag_uni_oprt_neg_complex(diag_infix_call_stack){
	RETURNDIAG( - arg->GetComplex() );
};

// Macro for auto generating simple assignments that have already overloaded operators
#define REG_SIMPLE_ASSIGN(name,getfunc) void diag_oprt_assign_##name##_2_##name(diag_assign_call_stack) { \
	dest = ( lhs->getfunc() ); \
}

REG_SIMPLE_ASSIGN( bool, GetBool );
REG_SIMPLE_ASSIGN( int, GetInteger );
REG_SIMPLE_ASSIGN( real, GetFloat );
REG_SIMPLE_ASSIGN( complex, GetComplex );
REG_SIMPLE_ASSIGN( string, GetString );
REG_SIMPLE_ASSIGN( array, GetArray );
void diag_oprt_assign_variable_2_variable(diag_assign_call_stack){
	dest = ( *(lhs.Get()) );
};

void diag_oprt_assign_handle_2_handle(diag_assign_call_stack){
	// we do special type checking here
	if( ! is_value_valid_handle( dest ) )
	{
		mpError("Cannot assign handle to type %s.", get_type_id(dest.GetType()));
	}
	dest = (  * (lhs.Get()) );
};

// macro for auto generating functions of the type +=, -=, etc
#define REG_COMPOUND_ASSIGN(name, type, getfunc, op) void diag_oprt_assign_##name##_##type(diag_assign_call_stack) { \
	dest = ( dest.getfunc() op lhs->getfunc() ); \
}

REG_COMPOUND_ASSIGN(add, int,  GetInteger, +);
REG_COMPOUND_ASSIGN(add, real,  GetFloat, +);
REG_COMPOUND_ASSIGN(add, complex, GetComplex, +);
REG_COMPOUND_ASSIGN(add, string, GetString, +);

REG_COMPOUND_ASSIGN(sub, int,  GetInteger, -);
REG_COMPOUND_ASSIGN(sub, real,  GetFloat, -);
REG_COMPOUND_ASSIGN(sub, complex, GetComplex, -);

REG_COMPOUND_ASSIGN(mul, int,  GetInteger, *);
REG_COMPOUND_ASSIGN(mul, real,  GetFloat, *);
REG_COMPOUND_ASSIGN(mul, complex, GetComplex, *);

REG_COMPOUND_ASSIGN(div, int,  GetInteger, /);
REG_COMPOUND_ASSIGN(div, real,  GetFloat, /);
REG_COMPOUND_ASSIGN(div, complex, GetComplex, /);

// the decrement and increment operators
void diag_oprt_self_assign_increment_int(diag_self_assign_call_stack){
	dest = ( dest.GetInteger() + 1 );
};

void diag_oprt_self_assign_decrement_int(diag_self_assign_call_stack){
	dest = ( dest.GetInteger() - 1 );
}

